
package fuzzyLogic;

import java.util.ArrayList;

/**
 * Klasa definiująca ogólne właściwości logiki rozmytej.
 * @author CygiDawid
 */
public class FuzzyLogic {
    private final ArrayList<LinguisticVariable> _variables;
    private int[] _rules1;
    private int[][] _rules2;
    private int[][][] _rules3;
    private int _inCount;
    
    
    public FuzzyLogic() {
        _variables = new ArrayList();
        _inCount = 0;
    }
    
    
    /**
     * Dodanie zmiennej ligwistycznej do logiki rozmytej
     * @param variable Zmienna lingwistyczna
     */
    public void addVariable(LinguisticVariable variable) {
        _variables.add(variable);
        _inCount = _variables.size() - 1;
    }
    
    
    /**
     * Oblicza wartość wyjściową dla 3 danych wejściowych
     * @param value1 Dana wejściowa dla pierwszej zmiennej lingwistycznej
     * @param value2 Dana wejściowa dla drugiej zmiennej lingwistycznej
     * @param value3 Dana wejściowa dla trzeciej zmiennej lingwistycznej
     * @return Wartość wyjściowa
     */
    public double getResult(int value1, int value2, int value3) {
        double[][] results = new double[_inCount][];
        
        results[0] = _variables.get(0).getAffiliations(value1);
        results[1] = _variables.get(1).getAffiliations(value2);
        results[2] = _variables.get(2).getAffiliations(value3);
        
        double[] results2 = new double[_variables.get(_inCount).getSetsCount()];
        for (int i = 0; i < results2.length; i++) {
            results2[i] = 0d;
        }
        
        for (int i = 0; i < results[0].length; i++) {
            if (results[0][i] > 0d) {
                for (int j = 0; j < results[1].length; j++) {
                    if (results[1][j] > 0d) {
                        for (int k = 0; k < results[2].length; k++) {
                            if (results[2][k] > 0d) {
                                int idx = _rules3[i][j][k];
                                
                                double value = Math.min(results[0][i], results[1][j]);
                                value = Math.min(value, results[2][k]);
                                
                                results2[idx] = Math.max(value, results2[idx]);
                            }
                        }
                    }
                }
            }
        }
        
        return _variables.get(_inCount).getCenterOfGravity(results2);
    }
    
    /**
     * Ustawia zasady w logice rozmytej z trzema zmiennymi wejściowymi
     * @param rules Zasady
     */
    public void setRulesFor3(int[][][] rules) {
        _rules3 = rules;
    }
    
    /**
     * Oblicza wartość wyjściową dla 2 danych wejściowych
     * @param value1 Dana wejściowa dla pierwszej zmiennej lingwistycznej
     * @param value2 Dana wejściowa dla drugiej zmiennej lingwistycznej
     * @return Wartość wyjściowa
     */
    public double getResult(int value1, int value2) {
        double[][] results = new double[_inCount][];
        
        results[0] = _variables.get(0).getAffiliations(value1);
        results[1] = _variables.get(1).getAffiliations(value2);
        
        double[] results2 = new double[_variables.get(_inCount).getSetsCount()];
        for (int i = 0; i < results2.length; i++) {
            results2[i] = 0d;
        }
        
        for (int i = 0; i < results[0].length; i++) {
            if (results[0][i] > 0d) {
                for (int j = 0; j < results[1].length; j++) {
                    if (results[1][j] > 0d) {
                        int idx = _rules2[i][j];
                        double value = Math.min(results[0][i], results[1][j]);
                        results2[idx] = Math.max(value, results2[idx]);
                    }
                }
            }
        }
        
        return _variables.get(_inCount).getCenterOfGravity(results2);
    }
    
    /**
     * Ustawia zasady w logice rozmytej z dwiema zmiennymi wejściowymi
     * @param rules Zasady
     */
    public void setRulesFor2(int[][] rules) {
        _rules2 = rules;
    }
    
    /**
     * Oblicza wartość wyjściową dla 1 danej wejściowej
     * @param value1 Dana wejściowa dla pierwszej zmiennej lingwistycznej
     * @return Wartość wyjściowa
     */
    public double getResult(int value1) {
        double[][] results = new double[_inCount][];
        
        results[0] = _variables.get(0).getAffiliations(value1);
        
        double[] results2 = new double[_variables.get(1).getSetsCount()];
        for (int i = 0; i < results2.length; i++) {
            results2[i] = 0d;
        }
        
        for (int i = 0; i < results[0].length; i++) {
            if (results[0][i] > 0d) {
                int idx = _rules1[i];
                results2[idx] = Math.max(results[0][i], results2[idx]);
            }
        }
        
        return _variables.get(1).getCenterOfGravity(results2);
    }
    
    /**
     * Ustawia zasady w logice rozmytej z jedną zmienną wejściową
     * @param rules Zasady
     */
    public void setRulesFor1(int[] rules) {
        _rules1 = rules;
    }
}
